﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace ConAppCore.Test
{
    unsafe internal class TestZPool
    {
        public static void Test()
        {
            //StringBuilder sb = new StringBuilder();
            //foreach (var item in typeof(int[]).GetInterfaces())
            //{
            //    sb.AppendLine(item.FullName);
            //}
            //string str = sb.ToString();

            //TestMemoryPool();
            //TestArrayPoolZFunc();
            TestMemoryPoolZPeromance();
            //TestArrayPoolPeromance();

            //Thread.Sleep(5000);
            //Console.WriteLine("--------- start ----------");
            //TestMemoryPoolZ();
            //TestMemoryPoolZPeromance2();
            //Console.WriteLine("-------------------");
            //TestMemoryPoolZManager();
        }

        private static int _seed = 1;

        private static void TestMemoryPoolZManager()
        {
            var options = new MemoryPoolZOption[]
            {
                new MemoryPoolZOption(100,100),
                new MemoryPoolZOption(500,100),
                new MemoryPoolZOption(1000,100),
                new MemoryPoolZOption(5000,100),
                new MemoryPoolZOption(10000,100),
                new MemoryPoolZOption(20000,100),
                new MemoryPoolZOption(30000,100),
                new MemoryPoolZOption(40000,100),
                new MemoryPoolZOption(50000,100),
                new MemoryPoolZOption(60000,100),
                new MemoryPoolZOption(70000,100),
            };
            using var poolManager = new MemoryPoolZManager<double>(options);

            int testCount = 10;
            Parallel.For(0, testCount, j =>
            {
                Random rand = new Random(Interlocked.Increment(ref _seed));
                int perTestCount = 10000;
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = poolManager.Rent(rand.Next(50, 65000));
                    SpanZ<double> data = memoryBlock.SpanZ;

                    for (int k = 0; k < data.Length; k++)
                    {
                        data[k] = rand.Next();
                    }

                    SpinWaiter.SpinWaitMilliseconds(1);
                }

                Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {j}/{testCount}");
            });
            Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} test over");
        }

        private static void TestMemoryPoolZ()
        {
            int blockAlignedSize = 65000;
            int testCount = 10;
            using var pool = new MemoryPoolZ<double>(blockAlignedSize, 100);

            Parallel.For(0, testCount, j =>
            {
                Random rand = new Random(Interlocked.Increment(ref _seed));
                int perTestCount = 10000;
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = pool.Rent(rand.Next(100, blockAlignedSize));
                    SpanZ<double> data = memoryBlock.SpanZ;

                    for (int k = 0; k < data.Length; k++)
                    {
                        data[k] = rand.Next();
                    }

                    SpinWaiter.SpinWaitMilliseconds(1);
                }

                Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {j}/{testCount}");
            });
            Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} test over");
        }



        private static void TestArrayPoolPeromance()
        {
            Stopwatch stopwatch = new Stopwatch();
            int perTestCount = 10000;
            int blockAlignedSize = 65000;

            List<double> values = new List<double>();
            int testCount = 100;

            var pool = new ArrayPoolZ<double>(blockAlignedSize, 100);
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = pool.Rent(blockAlignedSize);
                    ArraySpan<double> data = memoryBlock.Data;
                }
                stopwatch.Stop();
                values.Add(stopwatch.Elapsed.TotalMilliseconds);
            }
            Console.WriteLine($"test:{testCount}x{perTestCount},ArrayPoolZ:{Math.Round(values.Average(), 3)}ms");

            values.Clear();
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                for (int i = 0; i < perTestCount; i++)
                {
                    var memoryBlock = new double[blockAlignedSize];
                    ArraySpan<double> data = new ArraySpan<double>(memoryBlock);
                }
                stopwatch.Stop();
                values.Add(stopwatch.Elapsed.TotalMilliseconds);
            }
            Console.WriteLine($"test:{testCount}x{perTestCount},new double[]:{Math.Round(values.Average(), 3)}ms");
        }

        private static void TestArrayPoolZFunc()
        {
            int blockAlignedSize = 10;
            var pool = new ArrayPoolZ<double>(blockAlignedSize, 100);

            using var memoryBlock = pool.Rent(blockAlignedSize);
            ArraySpan<double> data = memoryBlock.Data;
            for (int i = 0; i < blockAlignedSize; i++)
            {
                data[i] = i * 10;
            }

            foreach (var itme in data)
            {
                Console.WriteLine(itme);
            }
        }

        private static void TestMemoryPoolZPeromance2()
        {
            Stopwatch stopwatch = new Stopwatch();
            int perTestCount = 10000;

            int[] blockAlignedSizes = new int[] { 80, 200, 800, 3000, 8000, 15000, 25000, 35000, 45000, 55000, 65000 };

            List<double> values = new List<double>();
            int testCount = 20;

            var options = new MemoryPoolZOption[]
            {
                new MemoryPoolZOption(100,100),
                new MemoryPoolZOption(500,100),
                new MemoryPoolZOption(1000,100),
                new MemoryPoolZOption(5000,100),
                new MemoryPoolZOption(10000,100),
                new MemoryPoolZOption(20000,100),
                new MemoryPoolZOption(30000,100),
                new MemoryPoolZOption(40000,100),
                new MemoryPoolZOption(50000,100),
                new MemoryPoolZOption(60000,100),
                new MemoryPoolZOption(70000,100),
            };
            using var pollManager = new MemoryPoolZManager<double>(options, BufferSearchAlgorithmType.Sequence);

            foreach (var blockAlignedSize in blockAlignedSizes)
            {
                values.Clear();
                for (int j = 0; j < testCount; j++)
                {
                    stopwatch.Restart();
                    for (int i = 0; i < perTestCount; i++)
                    {
                        using var memoryBlock = pollManager.Rent(blockAlignedSize);
                        Span<double> data = memoryBlock.Span;
                        for (int k = 0; k < data.Length; k++)
                        {
                            data[k] = k;
                        }
                    }
                    stopwatch.Stop();
                    values.Add(stopwatch.Elapsed.TotalMilliseconds);
                }
                Console.WriteLine($"memoryBlock.Span blockAlignedSize:{blockAlignedSize},test:{testCount}x{perTestCount},平均用时:{Math.Round(values.Average(), 3)}ms");
            }
            Console.WriteLine("----------------------------------------------------------------------------------");
            foreach (var blockAlignedSize in blockAlignedSizes)
            {
                values.Clear();
                for (int j = 0; j < testCount; j++)
                {
                    stopwatch.Restart();
                    for (int i = 0; i < perTestCount; i++)
                    {
                        using var memoryBlock = pollManager.Rent(blockAlignedSize);
                        SpanZ<double> data = memoryBlock.SpanZ;
                        for (int k = 0; k < data.Length; k++)
                        {
                            data[k] = k;
                        }
                    }
                    stopwatch.Stop();
                    values.Add(stopwatch.Elapsed.TotalMilliseconds);
                }
                Console.WriteLine($"memoryBlock.SpanZ blockAlignedSize:{blockAlignedSize},test:{testCount}x{perTestCount},平均用时:{Math.Round(values.Average(), 3)}ms");
            }
            Console.WriteLine("----------------------------------------------------------------------------------");
            foreach (var blockAlignedSize in blockAlignedSizes)
            {
                values.Clear();
                for (int j = 0; j < testCount; j++)
                {
                    stopwatch.Restart();
                    for (int i = 0; i < perTestCount; i++)
                    {
                        using var memoryBlock = pollManager.Rent(blockAlignedSize);
                        double* dataPtr = memoryBlock.DataPtr;
                        for (int k = 0; k < memoryBlock.Length; k++)
                        {
                            dataPtr[k] = k;
                        }
                    }
                    stopwatch.Stop();
                    values.Add(stopwatch.Elapsed.TotalMilliseconds);
                }
                Console.WriteLine($"memoryBlock.DataPtr blockAlignedSize:{blockAlignedSize},test:{testCount}x{perTestCount},平均用时:{Math.Round(values.Average(), 3)}ms");
            }
            Console.WriteLine("----------------------------------------------------------------------------------");
            foreach (var blockAlignedSize in blockAlignedSizes)
            {
                values.Clear();
                for (int j = 0; j < testCount; j++)
                {
                    stopwatch.Restart();
                    for (int i = 0; i < perTestCount; i++)
                    {
                        using var memoryBlock = pollManager.Rent(blockAlignedSize);
                        for (int k = 0; k < memoryBlock.Length; k++)
                        {
                            memoryBlock[k] = k;
                        }
                    }
                    stopwatch.Stop();
                    values.Add(stopwatch.Elapsed.TotalMilliseconds);
                }
                Console.WriteLine($"memoryBlock.Index blockAlignedSize:{blockAlignedSize},test:{testCount}x{perTestCount},平均用时:{Math.Round(values.Average(), 3)}ms");
            }
            Console.WriteLine("----------------------------------------------------------------------------------");
            var pool = System.Buffers.MemoryPool<double>.Shared;
            foreach (var blockAlignedSize in blockAlignedSizes)
            {
                values.Clear();
                for (int j = 0; j < testCount; j++)
                {
                    stopwatch.Restart();
                    for (int i = 0; i < perTestCount; i++)
                    {
                        using System.Buffers.IMemoryOwner<double> blockOwner = pool.Rent(blockAlignedSize);
                        Span<double> block = blockOwner.Memory.Span;
                        for (int k = 0; k < block.Length; k++)
                        {
                            block[k] = k;
                        }
                    }
                    stopwatch.Stop();
                    values.Add(stopwatch.Elapsed.TotalMilliseconds);
                }
                Console.WriteLine($"System.Buffers.MemoryPool blockAlignedSize:{blockAlignedSize},test:{testCount}x{perTestCount},平均用时:{Math.Round(values.Average(), 3)}ms");
            }

        }

        private static void TestMemoryPoolZPeromance()
        {
            Stopwatch stopwatch = new Stopwatch();
            int perTestCount = 10000;
            int blockAlignedSize = 65000;

            List<double> values = new List<double>();
            int testCount = 100;

            using var pool = new MemoryPoolZ<double>(blockAlignedSize, 100);
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = pool.Rent(blockAlignedSize);
                    SpanZ<double> data = memoryBlock.SpanZ;
                }
                stopwatch.Stop();
                values.Add(stopwatch.Elapsed.TotalMilliseconds);
            }
            Console.WriteLine($"test:{testCount}x{perTestCount},MemoryPoolZ:{Math.Round(values.Average(), 3)}ms");

            var options = new MemoryPoolZOption[]
            {
                new MemoryPoolZOption(100,100),
                new MemoryPoolZOption(500,100),
                new MemoryPoolZOption(1000,100),
                //new MemoryPoolZOption(5000,100),
                //new MemoryPoolZOption(10000,100),
                //new MemoryPoolZOption(20000,100),
                //new MemoryPoolZOption(30000,100),
                //new MemoryPoolZOption(40000,100),
                //new MemoryPoolZOption(50000,100),
                //new MemoryPoolZOption(60000,100),
                //new MemoryPoolZOption(70000,100),
            };
            using var pollManager = new MemoryPoolZManager<double>(options, BufferSearchAlgorithmType.Sequence);
            values.Clear();
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = pollManager.Rent(blockAlignedSize);
                    SpanZ<double> data = memoryBlock.SpanZ;
                }
                stopwatch.Stop();
                values.Add(stopwatch.Elapsed.TotalMilliseconds);
            }
            Console.WriteLine($"test:{testCount}x{perTestCount},MemoryPoolZManager:{Math.Round(values.Average(), 3)}ms");


            values.Clear();
            for (int j = 0; j < testCount; j++)
            {
                stopwatch.Restart();
                for (int i = 0; i < perTestCount; i++)
                {
                    using var memoryBlock = new UnmanagedMemoryPointer<double>(blockAlignedSize);
                    SpanZ<double> data = memoryBlock.GetSpanZ();
                }
                stopwatch.Stop();
                values.Add(stopwatch.Elapsed.TotalMilliseconds);
            }
            Console.WriteLine($"test:{testCount}x{perTestCount},UnmanagedMemoryPointer:{Math.Round(values.Average(), 3)}ms");


        }


        private static void TestMemoryPool()
        {
            var pool = System.Buffers.MemoryPool<int>.Shared;
            using System.Buffers.IMemoryOwner<int> blockOwner = pool.Rent(1000);
            Memory<int> block = blockOwner.Memory;
            //System.Buffers.MemoryManager
        }


        private static int BinarySearcch(int[] arr, int target)
        {
            int left = 0, right = arr.Length - 1;

            while (left <= right)
            {
                int mid = left + (right - left) / 2;
                int midValue = arr[mid];

                if (midValue == target)
                {
                    return mid;
                }
                else if (midValue < target)
                {
                    left = mid + 1;
                }
                else
                {
                    right = mid - 1;
                }
            }

            return -1;
        }
    }
}
